iT邦幫忙

2024 iThome 鐵人賽

DAY 18
1
佛心分享-SideProject30

用 Golang 實作 streamlit 系列 第 18

Day18: Delta Update: WebSocket

  • 分享至 

  • xImage
  •  

Day2 ,我們提到 Streamlit 的機制是可以在跑 Script 時更新 GUI 的:

import streamlit as st
import time
for i in range(5):
    time.sleep(1)
    st.text(str(i))

以之前的一口氣塞一包 JSON 給前端是無法達到的,因此,我們要使用 WebSocket 來做到這件事情。

構想的流程是這樣的:

  • 前端頁面載入,和後端連上。
  • 前端發起更新請求: 前端向後端發送一個 WebSocket 訊息,表示需要更新頁面。
  • 後端啟動 Goroutine: 後端收到請求後,會啟動一個新的 Goroutine 來執行對應的 Script。
  • 執行 Script: 在 Script 執行過程中,後端會不斷地將需要更新的组件訊息通過 WebSocket 推送到前端。
  • 前端更新頁面: 前端收到更新消息後,會根據消息中的訊息更新對應的组件,並重新渲染頁面。

後端

在本來,我們新增一個 Component 就會這樣

container.Comps = append(container.Comps, newComp)

我們需要改成直接送這資訊給前端:

{
  "parent_comp_id": "在哪個 component(container) 底下塞 component",
  "component": {component config}
}

所以我們在建立 Components 時,要準備好一個 Callback,用來替換上面的 append list 操作

sendNotifyPack := func(parentID string, comp Component) {
	// ...
	websocket.JSON.Send(ws, pack)
}

使用 Go golang.org/x/net/websocket 包來處理 WebSocket 連接。

  • 腳本執行: 當前端發來更新請求時,後端會啟動一個 Goroutine 來執行對應的 Script Function。
  • 消息推送: 在腳本執行過程中,後端會將需要更新的 Component (例如:组件類型、屬性、Parent Component ID) 通過 WebSocket 推送到前端。
  • Goroutine 管理: 如果有新的更新請求到來,後端會先中斷正在執行的 Goroutine,再啟動新的 Goroutine。

中斷部分我用 panic 做到這件事情。

sendNotifyPack := func(parentID string, comp Component) {
	if stopUpdating.Load() {
		panic(ErrUpdateInterrupt)
	}
	websocket.JSON.Send(ws, pack)
}

然後在外面把 panic recover 下來。

func (app *App) RunWithHandlingPanic(
	name string, state *State, notifyFunc SendNotifyPackFunc) (err error) {
	defer func() {
		r := recover()
		if r != nil {
			err = tgutil.Errorf("%w: %v", ErrPanic, r)
		}
	}()
	err = app.Run(name, state, notifyFunc)
	return
}

前端

要 Handle 的邏輯其實就是

  • Forest: 前端維護數個樹狀結構來表示頁面上的所有 Component。每個组件都有唯一的 ID,用於在樹中定位和更新。
  • WebSocket 連接: 前端與後端建立 WebSocket 連接,並監聽後端發來的消息。

更新機制: 當後端發來更新消息時,前端會根據消息中的訊息更新對應的组件,並讓 React 重新渲染頁面。

export class Node {
  props: any
  children: Node[]
  parentID: string
  constructor(props: any) {
    this.props = props
    this.children = []
    this.parentID = ''
  }
}

export class Forest {
  nodes: { [id: string]: Node }
  // ...
}

明天將介紹 StatefulWebsocket。


上一篇
Day17 Multipage
下一篇
Day19 StatefulWebsocket
系列文
用 Golang 實作 streamlit 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言